package com.hup.udptest.view;

import com.hup.udptest.model.AppConfig;
import com.hup.utils.commons.DateBuilder;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.commons.start.StartupInfo;
import com.hup.utils.commons.thread.ThreadPoolUtil;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.frame.BaseFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.swing.SwingUtilities;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author caigh
 * @date 2023/2/9
 */
@Slf4j
public class MainFrame extends BaseFrame {

    private final AppConfig config;
    private final ThreadPoolExecutor executor;
    private MainForm form;

    private boolean isRunningServer;
    private DatagramSocket receiverSocket;

    public MainFrame() {
        super(StartupInfo.getApplicationChName());
        config = new AppConfig().loadFromFile();
        executor = ThreadPoolUtil.fixedThreadExecutor("udp-server", 1, "udp-server");

        initView();
        initListener();
        try {
            setSubTitle("PID:" + StartupInfo.getPid() + ", " + InetAddress.getLocalHost());
        } catch (UnknownHostException e) {
            onExecuteException("获取本机地址异常:", e);
        }
    }

    private void initView() {
        form = new MainForm();
        setContentPane(form.root);
        LookThemeUtil.initFrameSize(this, 680, 580);
        setLocationRelativeTo(null);

        updateReceiverButton();
        form.txtReceiverPort.setText(config.receiverPort);
        form.txtSenderIp.setText(config.senderIp);
        form.txtSenderPort.setText(config.senderPort);
        form.txtSender.setText(config.toSend);
    }

    private void initListener() {
        form.btnReceiverStart.addActionListener(e -> doInBackground(e.getActionCommand(), () -> {
            String receiverPort = form.txtReceiverPort.getText();
            switchUdpReceiver(true, receiverPort);
        }));
        form.btnReceiverStop.addActionListener(e -> doInBackground(e.getActionCommand(), () -> {
            String receiverPort = form.txtReceiverPort.getText();
            switchUdpReceiver(false, receiverPort);
        }));
        form.btnReceiverClearLog.addActionListener(e -> form.txtReceiver.setText(""));
        form.btnSenderSend.addActionListener(e -> doInBackground(e.getActionCommand(), () -> {
            String senderIp = form.txtSenderIp.getText();
            String senderPort = form.txtSenderPort.getText();
            String toSend = form.txtSender.getText();
            sendToClient(senderIp, senderPort, toSend);
        }));
        form.btnClearLog.addActionListener(e -> form.txtLog.setText(""));
    }

    private void updateReceiverButton() {
        form.btnReceiverStart.setVisible(!isRunningServer);
        form.btnReceiverStop.setVisible(isRunningServer);
    }

    @Override
    public void onHidden(boolean dispose) {
        if (dispose) System.exit(0);
    }

    public void switchUdpReceiver(boolean isStart, String receiverPort) {
        if (isRunningServer == isStart) return;
        log.info("isStart={}", isStart);
        isRunningServer = isStart;
        if (isRunningServer) {
            updateReceiverButton();
            int port = Integer.parseInt(receiverPort);
            log.info("activeCount={}", executor.getActiveCount());
            executor.execute(() -> {
                log.info("线程启动");
                byte[] inputBuff = new byte[512];
                DatagramPacket inputPacket = new DatagramPacket(inputBuff, inputBuff.length);
                try {
                    receiverSocket = new DatagramSocket(port);
                    config.setReceiverPort(receiverPort);//连接成功,则保存入参
                    config.save();
                    appendLog("连接成功");

                    while (isRunningServer) {
                        inputPacket.setData(inputBuff);
                        receiverSocket.receive(inputPacket);//阻塞,等待数据

                        //收到数据
                        InetAddress address = inputPacket.getAddress();
                        int respPort = inputPacket.getPort();
                        int readLength = inputPacket.getLength();
                        if (readLength != 0) {
                            byte[] data = new byte[readLength];
                            System.arraycopy(inputBuff, 0, data, 0, readLength);
                            String receivedMsg = new String(data);

                            String logData = "from=" + address + ", length=" + readLength + ", msg=[" + receivedMsg + "]";
                            log.info("收到数据={}", logData);
                            ComponentUtil.appendAndScroll(form.txtReceiver, DateBuilder.ymdHms(new Date()) + ": " + logData + "\n");
                        }
                    }
                } catch (Exception e) {
                    if (isRunningServer) {
                        //非主动关闭
                        onExecuteException("接收端异常:", e);
                    }
                }
                appendLog("连接关闭");
                log.info("线程结束");
                isRunningServer = false;
                SwingUtilities.invokeLater(() -> updateReceiverButton());
            });
        } else {
            receiverSocket.close();
        }
    }

    private void sendToClient(String senderIp, String senderPort, String toSend) throws Throwable {
        if (!StringUtils.hasText(toSend)) return;
        InetAddress address;
        if (!StringUtils.hasText(senderIp)) {
            address = InetAddress.getLocalHost();
        } else {
            address = InetAddress.getByName(senderIp);
        }
        //log.info("address={}", address);
        int port = Integer.parseInt(senderPort);
        byte[] data = toSend.getBytes();

        DatagramSocket socket = new DatagramSocket();//占用随机端口号
        socket.setSoTimeout(1000);
        socket.send(new DatagramPacket(data, data.length, address, port));

        appendLog("发送成功");
        config.setSenderIp(senderIp);
        config.setSenderPort(senderPort);
        config.setToSend(toSend);
        config.save();
    }

    private void appendLog(String log) {
        ComponentUtil.appendAndScroll(form.txtLog, DateBuilder.ymdHms(new Date()) + log + "\n");
    }

}
